package sf.database;

import sf.core.DBObject;
import sf.database.meta.ColumnMapping;
import sf.database.meta.TableMapping;
import sf.spring.util.Assert;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 记录对象 动态表使用
 */
//使得该类能顺利通过JAXB的序列化
public final class VarObject extends DBObject implements Map<String, Object> {
    private static final long serialVersionUID = 3915258646897359358L;
    private final HashMap<String, Object> map = new HashMap<String, Object>();

    private TableMapping meta;

    protected VarObject() {
    }

    public VarObject(TableMapping meta) {
        Assert.notNull(meta, "");
        this.meta = meta;
    }

    public VarObject(TableMapping meta, boolean recordField) {
        this.meta = meta;
        this._recordUpdate = false;
    }


    /**
     * 得到一个字段值
     * @param key
     * @return
     */
    public Object get(String key) {
        super.beforeGet(key);
        return map.get(key);
    }

    /**
     * 得到字段值，并转换为List<VarObject>
     * @param key
     * @return 将类型强制转换为List<VarObject>并返回。 由于泛型擦除机制，并不能保证每个元素都是VarObject
     * @throws ClassCastException 当目标类型不能转换为List时
     */
    @SuppressWarnings("unchecked")
    public List<VarObject> getList(String key) {
        Object obj = get(key);
        if (obj == null)
            return Collections.emptyList();
        return (List<VarObject>) obj;
    }

    /**
     * 得到字段值，并转换为Collection<VarObject>
     * @param key
     * @return 将类型强制转换为Collection<VarObject>并返回。 由于泛型擦除机制，并不能保证每个元素都是VarObject
     * @throws ClassCastException 当目标类型不能转换为Collection时
     */
    @SuppressWarnings("unchecked")
    public Collection<VarObject> getCollection(String key) {
        Object obj = get(key);
        return (Collection<VarObject>) obj;
    }

    /**
     * 得到字段值，并转换为List<T>类型
     * @param key
     * @param clz
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> getList(String key, Class<T> clz) {
        Object obj = get(key);
        return (List<T>) obj;
    }

    /**
     * 得到字段值，并转换为Collection<T>
     * @param key
     * @return 将类型强制转换为Collection<T>并返回。 由于泛型擦除机制，并不能保证每个元素都是T
     * @throws ClassCastException 当目标类型不能转换为Collection时
     */
    @SuppressWarnings("unchecked")
    public <T> Collection<T> getCollection(String key, Class<T> clz) {
        Object obj = get(key);
        return (Collection<T>) obj;
    }


    /**
     * 得到字段值，类型转换为String
     * @param key
     * @return
     */
    public String getString(String key) {
        Object obj = get(key);
        if (obj == null)
            return null;
        return String.valueOf(obj);
    }

    public Byte getByte(String key) {
        Object obj = get(key);
        return (Byte) obj;
    }

    public Short getShort(String key) {
        Object obj = get(key);
        return (Short) obj;
    }

    /**
     * 得到字段值，类型转换为Integer
     * @param key
     * @return
     */
    public Integer getInteger(String key) {
        Object obj = get(key);
        return (Integer) obj;
    }

    /**
     * 得到字段值，类型转换为Long
     * @param key
     * @return
     */
    public Long getLong(String key) {
        Object obj = get(key);
        return (Long) obj;
    }

    /**
     * 得到字段值，类型转换为Double
     * @param key
     * @return
     */
    public Double getDouble(String key) {
        Object obj = get(key);
        return (Double) obj;
    }

    /**
     * 得到字段值，类型转换为Boolean
     * @param key
     * @return
     */
    public Boolean getBoolean(String key) {
        Object obj = get(key);
        return (Boolean) obj;
    }

    /**
     * 设置字段的值，增加了字段名和数据类型的校验，可以安全地设置数值
     * @param key
     * @param value
     * @return
     */
    public VarObject set(String key, Object value) {
        ColumnMapping field = meta.getIgnoreCaseDBFieldColumnMapping(key);
        if (field == null) {
            // Check field available
            if (!meta.getFields().containsKey(key)) {
                throw new IllegalArgumentException("Unknown field name '" + key + "' in table " + meta.getTableName() + ". Avaliable:" + meta.getFields().keySet());
            }
        } else {
            // Check the data type
            Class<?> expected = field.getClz();
            if (value != null && !expected.isAssignableFrom(value.getClass())) {
                throw new IllegalArgumentException("Field value not match the column type in database. field name '" + key + "' value is a '" + value.getClass().getSimpleName() + "'. expected is " + expected.getSimpleName());
            }
            if (_recordUpdate) {
                super.prepareUpdate(field.getField(), value);
            }
        }
        map.put(key, value);
        return this;
    }


    @Override
    public int size() {
        return map.size();
    }

    @Override
    public boolean isEmpty() {
        return map.isEmpty();
    }

    @Override
    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return map.containsValue(value);
    }

    @Override
    public Object get(Object key) {
        return map.get(key);
    }

    @Override
    public Object put(String key, Object value) {
        ColumnMapping field = meta.getIgnoreCaseDBFieldColumnMapping(key);
        if (field != null) {
            if (_recordUpdate) {
                super.prepareUpdate(field.getField(), value);
            }
        }
        return map.put(key, value);
    }

    @Override
    public Object remove(Object key) {
        ColumnMapping field = meta.getIgnoreCaseDBFieldColumnMapping(String.valueOf(key));
        if (field == null) {
            if (!meta.getFields().containsKey(key)) {
                throw new IllegalArgumentException("Unknown field name" + key);
            }
        } else {
            if (_recordUpdate) {
                super.prepareUpdate(field.getField(), null);
            }
        }
        return map.remove(key);
    }

    @Override
    public void putAll(Map<? extends String, ?> m) {
        map.putAll(m);
    }

    @Override
    public void clear() {
        map.clear();
    }

    @Override
    public Set<String> keySet() {
        return map.keySet();
    }

    @Override
    public Collection<Object> values() {
        return map.values();
    }

    @Override
    public Set<Entry<String, Object>> entrySet() {
        return map.entrySet();
    }

    @Override
    public int hashCode() {
        return map.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return map.equals(obj);
    }

    @Override
    public String toString() {
        return map.toString();
    }
}
